<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>首页轮播图</title>
  <style>
    .slider {
      position: relative;
      width: 790px;
      height: 340px;
    }

    .slider ul {
      list-style-type:none;
      position: relative;
      width: 100%;
      height: 100%;
      padding: 0;
      margin: 0;
    }

    .slider__item,
    .slider__item--selected {
      position: absolute;
      transition: opacity 1s;
      opacity: 0;
      text-align: center;
    }

    .slider__item--selected {
      transition: opacity 1s;
      opacity: 1;
    }

    .slider__next,
    .slider__previous{
      display: inline-block;
      position: absolute;
      top: 50%;
      margin-top: -25px;
      width: 30px;
      height:50px;
      text-align: center;
      font-size: 24px;
      line-height: 50px;
      overflow: hidden;
      border: none;
      background: transparent;
      color: white;
      background: rgba(0,0,0,0.2);
      cursor: pointer;
      opacity: 0;
      transition: opacity .5s;
    }

    .slider__previous {
      left: 0;
    }

    .slider__next {
      right: 0;
    }

    .slider:hover .slider__previous {
      opacity: 1;
    }


    .slider:hover .slider__next {
      opacity: 1;
    }

    .slider__previous:after {
      content: '<';
    }

    .slider__next:after {
      content: '>';
    }

    .slider__control{
      position: relative;
      display: table;
      background-color: rgba(255, 255, 255, 0.5);
      padding: 5px;
      border-radius: 12px;
      bottom: 30px;
      margin: auto;
    }

    .slider__control-buttons,
    .slider__control-buttons--selected{
      display: inline-block;
      width: 15px;
      height: 15px;
      border-radius: 50%;
      margin: 0 5px;
      background-color: white;
      cursor: pointer;
    }

    .slider__control-buttons--selected {
      background-color: red;
    }

  </style>
</head>
<body>
  <div class="slider"></div>
  <script>
    class Slider {
      constructor({container, images = [], cycle = 3000} = {}){
        this.container = container;
        this.data = images;
        this.container.innerHTML = this.render(this.data);
        this.items = Array.from(this.container.querySelectorAll('.slider__item, .slider__item--selected'));
        this.cycle = cycle;
        this.slideTo(0);
      }
      render(images){
        const content = images.map(image => `
          <li class="slider__item">
            <img src="${image}"/>
          </li>    
        `.trim());
        
        return `<ul>${content.join('')}</ul>`;
      }
      registerPlugins(...plugins){
        plugins.forEach(plugin => {
          const pluginContainer = document.createElement('div');
          pluginContainer.className = 'slider__plugin';
          pluginContainer.innerHTML = plugin.render(this.data);
          this.container.appendChild(pluginContainer);
          
          plugin.initialize(this);
        });
      }
      getSelectedItem(){
        const selected = this.container.querySelector('.slider__item--selected');
        return selected
      }
      getSelectedItemIndex(){
        return this.items.indexOf(this.getSelectedItem());
      }
      slideTo(idx){
        const selected = this.getSelectedItem();
        if(selected){ 
          selected.className = 'slider__item';
        }
        const item = this.items[idx];
        if(item){
          item.className = 'slider__item--selected';
        }

        const detail = {index: idx};
        const event = new CustomEvent('slide', {bubbles:true, detail});
        this.container.dispatchEvent(event);
      }
      slideNext(){
        const currentIdx = this.getSelectedItemIndex();
        const nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
      }
      slidePrevious(){
        const currentIdx = this.getSelectedItemIndex();
        const previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
        this.slideTo(previousIdx);  
      }
      start(){
        this.stop();
        this._timer = setInterval(()=>this.slideNext(), this.cycle);
      }
      stop(){
        clearInterval(this._timer);
      }
    }

    const pluginController = {
      render(images){
        return `
          <div class="slider__control">
            ${images.map((image, i) => `
                <span class="slider__control-buttons${i===0?'--selected':''}"></span>
            `).join('')}
          </div>    
        `.trim();
      },
      initialize(slider){
        const controller = slider.container.querySelector('.slider__control');
        
        if(controller){
          const buttons = controller.querySelectorAll('.slider__control-buttons, .slider__control-buttons--selected');
          controller.addEventListener('mouseover', evt => {
            const idx = Array.from(buttons).indexOf(evt.target);
            if(idx >= 0){
              slider.slideTo(idx);
              slider.stop();
            }
          });

          controller.addEventListener('mouseout', evt => {
            slider.start();
          });

          slider.container.addEventListener('slide', evt => {
            const idx = evt.detail.index
            const selected = controller.querySelector('.slider__control-buttons--selected');
            if(selected) selected.className = 'slider__control-buttons';
            buttons[idx].className = 'slider__control-buttons--selected';
          });
        }    
      }
    };

    const pluginPrevious = {
      render(){
        return `<a class="slider__previous"></a>`;
      },
      initialize(slider){
        const previous = slider.container.querySelector('.slider__previous');
        if(previous){
          previous.addEventListener('click', evt => {
            slider.stop();
            slider.slidePrevious();
            slider.start();
            evt.preventDefault();
          });
        }  
      }
    };

    const pluginNext = {
      render(){
        return `<a class="slider__next"></a>`;
      },
      initialize(slider){
        const previous = slider.container.querySelector('.slider__next');
        if(previous){
          previous.addEventListener('click', evt => {
            slider.stop();
            slider.slideNext();
            slider.start();
            evt.preventDefault();
          });
        }  
      }
    };

    const images = ['https://p5.ssl.qhimg.com/t0119c74624763dd070.png',
     'https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg',
     'https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg',
     'https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg'];

    const container = document.querySelector('.slider');
    const slider = new Slider({container, images});
    slider.registerPlugins(pluginController, pluginPrevious, pluginNext);
    slider.start();
  </script>
</body>
</html>